#pragma once
#include "constant.h"
#include <unordered_map>
#include <memory>
#include <websocketpp/common/connection_hdl.hpp>
#include <vector>
#include <nlohmann/json.hpp>
#include "config.h"

// namespace websocketpp
// {
//     using ccc = connection_hdl;
// }
namespace dymy
{
    namespace table
    {
        template <card_size_t PN, card_size_t HN>
        class CardTable;
    }

    struct User
    {
        ID_t id;
        std::string user_name;
        ID_t last_online_table_id;
        websocketpp::connection_hdl connection;
        bool notify;
    };

    class DBManager
    {
    private:
        typedef std::unordered_map<std::string, ID_t> Table;
        Table user_table_;

    public:
        DBManager() : user_table_() {}
        inline ID_t insert_user(const std::string &user_name)
        {
            ID_t id = user_table_.size() + 1;
            user_table_.insert(std::make_pair(user_name, id));
            return id;
        }

        inline ID_t query_user(const std::string &user_name)
        {
            auto it = user_table_.find(user_name);
            if (it == user_table_.cend())
            {
                return 0;
            }
            return it->second;
        }
    };

    class UserManager
    {
    public:
        typedef std::shared_ptr<User> UserSPtr;
        typedef std::unordered_map<ID_t, UserSPtr> online_users;
        UserManager();
        /*
     * >0... 用户id
     * */
        UserSPtr login(const std::string &user_name);

        UserSPtr get_user(ID_t id);
        online_users get_users() const;

        void logout(ID_t id);

    private:
        online_users m_users;
        std::unique_ptr<DBManager> db_;
    };

    class TableManager
    {
    public:
        typedef table::CardTable<dymy::config::pN, dymy::config::hN> CardTable;
        typedef CardTable *CardTablePtr;
        typedef std::unique_ptr<CardTable> CardTableUPtr;
        typedef std::vector<card_t> HoleCards;

    private:
        class TableManagerImpl;
        std::unique_ptr<TableManagerImpl> impl_;
        CardTablePtr get_table(ID_t table_id);

    public:
        TableManager();
        TableManager(TableManager &&);
        TableManager &operator=(TableManager &&);

        /*
     * -2000 用户不存在
     * -2001 牌桌不存在
     * -2002 位置索引错误
     * -2003 该位置已经有人
     * -2004 已经在该位置上了，加入失败
     * -2006 不在该位置上，退出失败
     * -2008 你已经有加入过的牌桌了，加入失败
     * ERROR_PROCESS 流程错误，这个牌桌不处于等待、完成状态的时候
     * 1 成功
     * */
        int join_or_quit_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool join);

        // int quit_table_user(ID_t user_id, ID_t &table_id_result, size_t &index_result);

        /*
     * -2000 用户不存在
     * -2001 牌桌不存在
     * -2002 位置索引错误
         * ERROR_EMPTY_PERSON 该位置不存在该用户
         * ERROR_PROCESS 流程错误
     * -2005:已经准备过了
     * -2007:已经处于空闲了
     *     //1:准备成功
        //0:取消准备成功
        //
     * */
        int prepared_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool prepared);

        /*
     * -2000 用户不存在
     * -2001 牌桌不存在
     * -2002 位置索引错误
         * ERROR_EMPTY_PERSON 该位置不存在该用户
         * ERROR_PROCESS 流程错误
         * ERROR_NOT_SELF 没轮到该用户操作

     * 0:不抢，1:抢地主成功 ,2:都不抢地主
     * */

        int rob_landlord_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool rob);

        /*
     * -2000 用户不存在
     * -2001 牌桌不存在
     * -2002 位置索引错误
     * -2003 该位置已经有人
         * ERROR_EMPTY_PERSON 该位置不存在该用户
         * ERROR_PROCESS 流程错误
         * ERROR_NOT_SELF 没轮到该用户操作
         * ERROR_CARDS 出的牌不对，或者首次地主不允许出空牌
     *  0:要不起
        1:出牌成功
        2:游戏结束
     * */
        int play_cards_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, const std::vector<dymy::card_t> &cards);

        ssize_t find_free_position(ID_t table_id) noexcept;

        ssize_t stop_table_if_user_quit(ID_t table_id, ID_t user_id);

        ID_t find_table_user(ID_t table_id, size_t table_index) noexcept(false);

        size_t get_person_num() noexcept;

        bool try_start(ID_t table_id);

        size_t get_table_num() noexcept;

        bool contains_user_in_table(ID_t table_id, ID_t user_id) noexcept;

        bool fill_table_info(nlohmann::json::object_t &j_data, ID_t table_id);

        bool fill_server_info(nlohmann::json::array_t &j_data);

        ssize_t find_user_index(ID_t table_id, ID_t user_id) noexcept;

        bool exist_table(ID_t table_id) noexcept;

        const multiple_card_t &find_table_user_cards(ID_t table_id, size_t index) noexcept(false);

        const HoleCards &find_table_hole_cards(ID_t table_id) noexcept(false);

        ~TableManager();
    };

}